<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .container {
            overflow: auto;
            margin: 10px;
        }

        ul {
            background-color: lightcoral;
        }
    </style>
</head>

<body>
    <div class="container">
    </div>

    <script type="text/javascript">
        // 后端数据
        const data = new Array(9999999).fill(0).map((_, i) => i);
        // 配置
        const bef = 10, // 前后可多查看的条数
            aft = 10,
            container = document.querySelector('.container'),
            containerHeihgt = 500,
            perHeight = 30,
            totalHeight = perHeight * data.length;
        // 可视数量
        const num = Math.ceil(containerHeihgt / perHeight);

        let st = 0,
            end = num;

        init();



        function init() {
            container.style.height = containerHeihgt + 'px';
            createDOM();
            bindEvent();
        }

        function bindEvent() {
            container.onscroll = throttle(onScroll);
        }

        function onScroll() {
            const [stIndex, endIndex] = getIndex(this.scrollTop);
            setData(stIndex, endIndex);
            // console.log({ stIndex, endIndex, top: this.scrollTop });
            setPos();
        }

        function setData(stIndex, endIndex) {
            st = stIndex;
            end = endIndex;

            createDOM();
        }

        function setPos() {
            const ul = container.querySelector('ul');
            ul.style.transform = `translateY(${(st) * perHeight}px)`;
        }

        function getIndex(top) {
            end = Math.ceil((top + containerHeihgt) / perHeight) + aft;
            st = Math.floor(top / perHeight) - bef; // `ul`的`margin` 可能让`st`不准 所以一定要初始化样式

            st < 0 && (st = 0);
            return [st, end];
        }

        function createDOM() {
            const newData = data.slice(st, end + 1),
                lis = newData.map((item) => `<li style="height: ${perHeight}px">${item}</li>`);

            const innerHTML = `<ul style="height: ${totalHeight}px">${lis.join('')}</ul>`;
            container.innerHTML = innerHTML;
        }

        function throttle(fn, duration = 50) {
            let st = Date.now();

            return function () {
                const now = Date.now();
                if (now - st >= duration) {
                    st = now;
                    return fn.apply(this, arguments);
                }
            };
        }
    </script>
</body>

</html>